package mlog

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"runtime"
	"sync"
	"time"
)

const (
	LevelFatal = iota
	LevelPanic
	LevelError
	LevelWarn
	LevelInfo
	LevelDebug
)

var levels = []string{
	"[FATAL]",
	"[PANIC]",
	"[ERROR]",
	"[WARN]",
	"[INFO]",
	"[DEBUG]",
}

type Logger struct {
	mu    sync.Mutex
	level int
	out   io.Writer
	buf   bytes.Buffer
	file  *os.File
}

func NewLogger(out io.Writer, lv int) *Logger {
	log := &Logger{out: out, level: lv, file: nil}
	log.buf.Grow(4096)
	runtime.SetFinalizer(log, CloseLogger)
	return log
}

func CloseLogger(log *Logger) {
	if log.file != nil {
		_ = log.file.Close()
		log.file = nil
		log.out = os.Stderr
	}
}

func (log *Logger) Output(level int, s string) error {
	if level > log.level {
		return nil
	}
	now := time.Now()
	log.mu.Lock()
	defer log.mu.Unlock()

	log.buf.Reset()
	log.buf.WriteString(now.Format("2006-01-02 15:04:05"))
	log.buf.WriteString(levels[log.level])
	log.buf.WriteString(s)

	if len(s) > 0 && s[len(s)-1] != '\n' {
		log.buf.WriteByte('\n')
	}

	_, err := log.out.Write(log.buf.Bytes())
	return err
}

func (log *Logger) Debugf(format string, v ...interface{}) {
	_ = log.Output(LevelDebug, fmt.Sprintf(format, v...))
}

func (log *Logger) Debug(v ...interface{}) {
	_ = log.Output(LevelDebug, fmt.Sprintln(v...))
}

func (log *Logger) Infof(format string, v ...interface{}) {
	_ = log.Output(LevelInfo, fmt.Sprintf(format, v...))
}

func (log *Logger) Info(v ...interface{}) {
	_ = log.Output(LevelInfo, fmt.Sprintln(v...))
}

func (log *Logger) Warnf(format string, v ...interface{}) {
	_ = log.Output(LevelWarn, fmt.Sprintf(format, v...))
}

func (log *Logger) Warn(v ...interface{}) {
	_ = log.Output(LevelWarn, fmt.Sprintln(v...))
}

func (log *Logger) Errorf(format string, v ...interface{}) {
	_ = log.Output(LevelError, fmt.Sprintf(format, v...))
}

func (log *Logger) Error(v ...interface{}) {
	_ = log.Output(LevelError, fmt.Sprintln(v...))
}

func (log *Logger) Fatalf(format string, v ...interface{}) {
	_ = log.Output(LevelFatal, fmt.Sprintf(format, v...))
	os.Exit(1)
}

func (log *Logger) Fatal(v ...interface{}) {
	_ = log.Output(LevelFatal, fmt.Sprintln(v...))
	os.Exit(1)
}

func (log *Logger) Panicf(format string, v ...interface{}) {
	s := fmt.Sprintf(format, v...)
	_ = log.Output(LevelPanic, s)
	panic(s)
}

func (log *Logger) Panic(v ...interface{}) {
	s := fmt.Sprintln(v...)
	_ = log.Output(LevelPanic, s)
	panic(s)
}

//设置日志文件输出
func (log *Logger) SetLogFile(fileDir string, fileName string) {
	var file *os.File

	//创建日志文件夹
	_ = mkdirLog(fileDir)

	fullPath := fileDir + "/" + fileName
	if checkFileExist(fullPath) {
		file, _ = os.OpenFile(fullPath, os.O_APPEND|os.O_RDWR, 0644)
	} else {
		file, _ = os.OpenFile(fullPath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644)
	}

	log.mu.Lock()
	defer log.mu.Unlock()

	if log.file != nil {
		log.file.Close()
	}
	log.file = file
	log.out = file
}

//判断日志文件是否存在
func checkFileExist(filename string) bool {
	exist := true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}

//创建目录
func mkdirLog(dir string) (e error) {
	_, er := os.Stat(dir)
	b := er == nil || os.IsExist(er)
	if !b {
		if err := os.MkdirAll(dir, 0775); err != nil {
			if os.IsPermission(err) {
				e = err
			}
		}
	}
	return
}
